
/* ======================================================
* 
* file:		file_monitor.cpp
* brief:	
* author:	80070525(chenjian)
* version:	1.0.0
* date:		2015-03-05 15:52:12
* 
* ======================================================*/
#include "file_monitor.h"
#include <sys/inotify.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <memory>

FileMonitor::FileMonitor(bool detach)
:is_detach_(detach), notify_fd_(-1), thread_(NULL)
{
	init();
}

int FileMonitor::addWatch(const std::string& file, const FileHandle& handle)
{
	if(notify_fd_ < 0)	
		return -1;

	//只做修改监听
	int watch_fd = inotify_add_watch(notify_fd_, file.c_str(), IN_MODIFY);
	if(watch_fd < 0)
		return -1;
	
	std::lock_guard<std::mutex> guard(mutex_);
	fd2Files_[watch_fd] = file;
	file2Handles_[file] = handle;
}

void FileMonitor::delWatch(const std::string& file)
{
	int watch_fd = -1;
	std::lock_guard<std::mutex> guard(mutex_);
	auto iter = fd2Files_.begin();
	for(; iter != fd2Files_.end(); ++iter)
	{
		if(file == iter->second)
		{
			watch_fd = iter->first;
			break;
		}
	}
	if(-1 != watch_fd)
		inotify_rm_watch(notify_fd_, watch_fd);
}

void FileMonitor::start()
{
	thread_ = new std::thread(std::bind(&FileMonitor::monitor, this));

	if(is_detach_)
		thread_->detach();
	else
		thread_->join();
}

void FileMonitor::init()
{
	if(-1 == notify_fd_)
	{
		notify_fd_ = inotify_init();
		
		//非阻塞
		fcntl(notify_fd_, F_SETFL, fcntl(notify_fd_, F_GETFL, 0)|O_NONBLOCK);	
	}
}

void FileMonitor::monitor()
{
	char buf[1024] ={'\0'};
	while(1)
	{
		fd_set fd_sets;
		FD_ZERO(&fd_sets);
		FD_SET(notify_fd_, &fd_sets);
		
		int rst = select(notify_fd_ + 1, &fd_sets, NULL, NULL, NULL);
		if(rst > 0)
		{
			int len , index = 0;
			while((len = read(notify_fd_, buf, 1023)) < 0 && errno == EINTR);
			while(index < len)
			{
				inotify_event* event = (inotify_event*)(buf + index);
				FileHandle handle;	
				if(getHandle(event->wd, handle))
					handle();

				index += sizeof(inotify_event) + event->len;
			}
		}
	}
}

bool FileMonitor::getHandle(const std::string& file, FileHandle& handle)
{
	std::lock_guard<std::mutex> guard(mutex_);
	auto iter = file2Handles_.find(file);	
	if(iter != file2Handles_.end())
	{
		handle = iter->second;
		return true;
	}
	return false;
}

bool FileMonitor::getHandle(int watchfd, FileHandle& handle)
{
	std::string file;
	{
		std::lock_guard<std::mutex> guard(mutex_);
		auto iter = fd2Files_.find(watchfd);
		if(iter != fd2Files_.end())
			file = iter->second;
	}
	
	return getHandle(file, handle);
}

